package xutil

import (
	"fmt"
)

type ReaderBuf struct {
	bs []byte
	n  int
}

func NewReaderBuf(bs []byte) *ReaderBuf {
	rb := &ReaderBuf{
		bs: bs,
		n:  0,
	}
	return rb
}

func (rb *ReaderBuf) ReadByte() (r byte, e error) {
	if rb.IsReadEnd() {
		e = fmt.Errorf("ReadByte out range len:%d, n:%d", len(rb.bs), rb.n)
		return
	}
	r = rb.bs[rb.n]
	rb.n++
	return
}

func (rb *ReaderBuf) ReadUInt16() (r uint16, e error) {
	if !rb.isCanRead(2) {
		e = fmt.Errorf("ReadInt16 out range len:%d, n:%d", len(rb.bs), rb.n)
		return
	}
	r = uint16(rb.bs[rb.n]) | (uint16(rb.bs[rb.n+1]) << 8)
	rb.n += 2
	return
}
func (rb *ReaderBuf) ReadUInt32() (r uint32, e error) {
	if !rb.isCanRead(4) {
		e = fmt.Errorf("ReadInt32 out range len:%d, n:%d", len(rb.bs), rb.n)
		return
	}
	r = uint32(rb.bs[rb.n]) | (uint32(rb.bs[rb.n+1]) << 8) | (uint32(rb.bs[rb.n+2]) << 16) | (uint32(rb.bs[rb.n+3]) << 24)
	rb.n += 4
	return
}

func (rb *ReaderBuf) ReadBytes() (bs []byte, e error) {
	if !rb.isCanRead(2) {
		e = fmt.Errorf("ReadBytes len out range 1.")
		return
	}
	bLen, _ := rb.ReadUInt16()
	if !rb.isCanRead(int(bLen)) {
		e = fmt.Errorf("ReadBytes len out range 2.")
	}
	bs = make([]byte, int(bLen))
	copy(bs, rb.bs[rb.n:rb.n+int(bLen)])
	rb.n += int(bLen)
	return
}
func (rb *ReaderBuf) ReadStringEN() (r string, e error) {
	if rb.IsReadEnd() {
		e = fmt.Errorf("ReadStringEN len out range 1.")
		return
	}
	sLen := int(rb.bs[rb.n])
	rb.n++
	if sLen <= 0 {
		e = fmt.Errorf("ReadStringEN string len:%d error", sLen)
		return
	}
	if !rb.isCanRead(sLen) {
		e = fmt.Errorf("ReadStringEN len out range 2.")
		return
	}
	bs := make([]byte, sLen)
	copy(bs, rb.bs[rb.n:rb.n+sLen])
	rb.n += sLen
	r = string(bs)
	return
}
func (rb *ReaderBuf) IsReadEnd() bool {
	return rb.n >= len(rb.bs)
}
func (rb *ReaderBuf) isCanRead(ct int) bool {
	return rb.n+ct <= len(rb.bs)
}

type WriterBuf struct {
	bs []byte
}

func NewWriterBuf(size int) *WriterBuf {
	wb := &WriterBuf{
		bs: make([]byte, 0, size),
	}
	return wb
}

func (wb *WriterBuf) WriteByte(v byte) {
	wb.bs = append(wb.bs, v)
}

func (wb *WriterBuf) WriteUInt16(v uint16) {
	wb.bs = append(wb.bs, byte(v&0xff))
	wb.bs = append(wb.bs, byte((v&0xff00)>>8))
}
func (wb *WriterBuf) WriteUInt32(v uint32) {
	wb.bs = append(wb.bs, byte(v&0xff))
	wb.bs = append(wb.bs, byte((v&0xff00)>>8))
	wb.bs = append(wb.bs, byte((v&0xff0000)>>16))
	wb.bs = append(wb.bs, byte((v&0xff000000)>>24))
}

func (wb *WriterBuf) WriteStringEN(v string) {
	len := byte(len(v))
	wb.bs = append(wb.bs, len)
	bs := []byte(v)
	wb.bs = append(wb.bs, bs...)
}

func (wb *WriterBuf) WriteBytes(bs []byte) {
	wb.WriteUInt16(uint16(len(bs)))
	wb.bs = append(wb.bs, bs...)
}

func (wb *WriterBuf) Bytes() []byte {
	return wb.bs
}
